package com.yzq.os.spider.v.service.queryurl.impl;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.log4j.Logger;

import com.yzq.os.spider.v.domain.QueryURL;
import com.yzq.os.spider.v.domain.SearchEngine;
import com.yzq.os.spider.v.domain.SearchEngineParam;
import com.yzq.os.spider.v.service.domain.SearchEngineParamService;
import com.yzq.os.spider.v.service.domain.SearchEngineService;
import com.yzq.os.spider.v.service.queryurl.CreateQueryURL;

/**
 * 默认排列组合生成搜索URL类
 * 
 * @author 苑志强(xingyu_yzq@163.com)
 * 
 */
public class DefaultCreateQueryURLService implements CreateQueryURL {

	protected Logger logger = Logger.getLogger(getClass());

	private SearchEngineService searchEngineService;

	private SearchEngineParamService searchEngineParamService;

	@Override
	public void setSearchEngineService(SearchEngineService searchEngineService) {
		this.searchEngineService = searchEngineService;
	}

	@Override
	public void setSearchEngineParamService(
			SearchEngineParamService searchEngineParamService) {
		this.searchEngineParamService = searchEngineParamService;
	}

	@Override
	public List<QueryURL> createInitQueryUrls(int searchEngineId)
			throws UnsupportedEncodingException {
		// Get need params
		SearchEngine searchEngine = searchEngineService
				.findById(searchEngineId);
		List<SearchEngineParam> singleValueRequired = searchEngineParamService
				.findSingleValueRequired(searchEngineId);
		List<SearchEngineParam> multipleValueRequired = searchEngineParamService
				.findMultipleValueRequired(searchEngineId);
		// auto create init query urls String.
		logger.info("singleValueRequired.size:"
				+ CollectionUtils.size(singleValueRequired));
		logger.info("multipleValueRequired.size:"
				+ CollectionUtils.size(multipleValueRequired));
		List<String> queryUrls = createInitQueryUrls(searchEngine,
				singleValueRequired, multipleValueRequired);
		// wrap query url Strings to Objects.
		List<QueryURL> urls = new ArrayList<QueryURL>();
		for (String queryUrl : queryUrls) {
			urls.add(new QueryURL(searchEngineId, queryUrl, queryUrl));
		}
		return urls;
	}

	protected List<String> createInitQueryUrls(SearchEngine searchEngine,
			List<SearchEngineParam> singleValueRequired,
			List<SearchEngineParam> multipleValueRequired)
			throws UnsupportedEncodingException {
		String searchEngineBaseUrl = searchEngine.getBaseUrl();
		List<String> singleValueRequiredQueryURI = queryURIParts(searchEngine,
				singleValueRequired);
		logger.info("singleValueRequiredQueryURI.size:"
				+ CollectionUtils.size(singleValueRequiredQueryURI));
		List<String> multipleValueRequiredQueryURI = queryURIParts(
				searchEngine, multipleValueRequired);
		logger.info("multipleValueRequiredQueryURI.size:"
				+ CollectionUtils.size(multipleValueRequiredQueryURI));
		List<String> result = createInitQueryUrls(
				searchEngineBaseUrl,
				CollectionUtils.isNotEmpty(singleValueRequiredQueryURI) ? singleValueRequiredQueryURI
						.get(0) : "", multipleValueRequiredQueryURI);
		return result;
	}

	protected List<String> createInitQueryUrls(String searchEngineBaseUrl,
			String singleValueQueryURIPart,
			List<String> multipleValueRequiredQueryURIParts) {
		List<String> result = new ArrayList<String>();
		// replace start '&' to '?'
		if (StringUtils.startsWith(singleValueQueryURIPart, "&")) {
			singleValueQueryURIPart = StringUtils.substringAfter(
					singleValueQueryURIPart, "&");
			singleValueQueryURIPart = "?" + singleValueQueryURIPart;
		}
		if (CollectionUtils.isNotEmpty(multipleValueRequiredQueryURIParts)) {
			for (String multipleValueRequiredQueryURIPart : multipleValueRequiredQueryURIParts) {
				// String queryUrl = searchEngineBaseUrl +
				// singleValueQueryURIPart + multipleValueRequiredQueryURIPart;
				StringBuffer queryUrl = new StringBuffer();
				queryUrl.append(searchEngineBaseUrl);
				if (StringUtils.isNotBlank(multipleValueRequiredQueryURIPart)) {
					if (StringUtils.isNotBlank(singleValueQueryURIPart)) {
						queryUrl.append(singleValueQueryURIPart);
						queryUrl.append(multipleValueRequiredQueryURIPart);
					} else {
						queryUrl.append("?");
						queryUrl.append(StringUtils.substringAfter(
								multipleValueRequiredQueryURIPart, "&"));
					}
				} else {
					if (StringUtils.isNotBlank(singleValueQueryURIPart)) {
						queryUrl.append(singleValueQueryURIPart);
					}
				}
				result.add(queryUrl.toString());
			}
		} else {
			String queryUrl = searchEngineBaseUrl + singleValueQueryURIPart;
			logger.info("searchEngineBaseUrl:[" + searchEngineBaseUrl
					+ "],singleValueQueryURIPart:[" + singleValueQueryURIPart
					+ "]");
			result.add(queryUrl);
		}
		return result;
	}

	@Override
	public List<QueryURL> generateQualifiedURLs(QueryURL queryURL)
			throws UnsupportedEncodingException {
		List<QueryURL> result = new ArrayList<QueryURL>();
		int searchEngineId = queryURL.getSearchEngineId();
		SearchEngine searchEngine = searchEngineService
				.findById(searchEngineId);
		String queryURLString = queryURL.getSpellUrl();
		List<SearchEngineParam> multipleValueNoRequired = searchEngineParamService
				.findMultipleValueNoRequired(searchEngineId);
		if (CollectionUtils.isEmpty(multipleValueNoRequired)) {
			logger.error("No config multiple value qualification params no required. need config. getSpellUrl:["
					+ queryURL.getSpellUrl() + "]");
			setQueryUrlDoFlag(queryURL,
					QueryURL.ERROR_NO_REQUIRED_MULTIPLE_VALUE_PARAM);
		} else {
			SearchEngineParam currentQualificationParam = currentQualificationParam(
					multipleValueNoRequired, queryURLString);
			SearchEngineParam nextQualificationParam = nextQualificationParam(
					multipleValueNoRequired, currentQualificationParam);
			if (nextQualificationParam != null) {
				List<SearchEngineParam> params = new ArrayList<SearchEngineParam>();
				params.add(nextQualificationParam);
				List<String> queryURIParts = queryURIParts(searchEngine, params);

				for (String queryURIPart : queryURIParts) {
					String url = queryURLString + queryURIPart;
					result.add(new QueryURL(searchEngineId, url, url));
				}
				logger.info("Qualification new create url size:["
						+ CollectionUtils.size(result) + "]");
				setQueryUrlDoFlag(queryURL, QueryURL.OK);
			} else {
				logger.error("No have next qualification param for url:["
						+ queryURLString + "], don't qualification.");
				setQueryUrlDoFlag(queryURL,
						QueryURL.ERROR_NO_NEXT_QUALIFICATION_PARAM);
			}
		}
		return result;
	}

	protected SearchEngineParam currentQualificationParam(
			List<SearchEngineParam> multipleValueNoRequired, String queryURL) {
		SearchEngineParam result = null;
		String[] paramNames = StringUtils.substringsBetween(queryURL, "&", "=");
		String lastParamName = paramNames[paramNames.length - 1];
		for (SearchEngineParam param : multipleValueNoRequired) {
			if (param.getName().equals(lastParamName)) {
				result = param;
			}
		}
		return result;
	}

	protected SearchEngineParam nextQualificationParam(
			List<SearchEngineParam> multipleValueNoRequired,
			SearchEngineParam currentQualificationParam) {
		SearchEngineParam result = null;
		if (currentQualificationParam == null) {
			// the first qualification parma
			result = multipleValueNoRequired.get(0);
		} else {
			// current no first . return next param or null(the last).
			boolean finded = false;
			for (SearchEngineParam param : multipleValueNoRequired) {
				if (finded) {
					result = param;
					break;
				}
				if (param.getId().equals(currentQualificationParam.getId())) {
					finded = true;
				}
			}
		}
		logger.debug("Next qualification param is:["
				+ ToStringBuilder.reflectionToString(result) + "]");
		return result;
	}

	protected List<String> queryURIParts(SearchEngine searchEngine,
			List<SearchEngineParam> searchEngineParamList)
			throws UnsupportedEncodingException {
		List<List<String>> wrapedMultipleValueParams = wrapMultipleValueParamList(
				searchEngine, searchEngineParamList);
		List<String> queryURIs = new ArrayList<String>();
		if (CollectionUtils.isNotEmpty(wrapedMultipleValueParams)) {
			recursivePermutation(wrapedMultipleValueParams,
					wrapedMultipleValueParams.get(0), "", queryURIs);
		}
		return queryURIs;
	}

	protected List<List<String>> wrapMultipleValueParamList(
			SearchEngine searchEngine, List<SearchEngineParam> needSpellParams)
			throws UnsupportedEncodingException {
		List<List<String>> paramsList = new ArrayList<List<String>>();
		for (SearchEngineParam needSpellParam : needSpellParams) {
			String name = needSpellParam.getName();
			List<String> values = searchEngineParamService.getValueList(
					needSpellParam.getValue(), searchEngine);
			List<String> params = new ArrayList<String>();
			for (String value : values) {
				params.add(getQueryString(name, value));
			}
			paramsList.add(params);
		}
		return paramsList;
	}

	protected void recursivePermutation(List<List<String>> paramsList,
			List<String> currParams, String queryUri, List<String> queryUris) {
		for (int i = 0; i < paramsList.size(); i++) {
			if (i == paramsList.indexOf(currParams)) {
				for (String param : currParams) {
					param = queryUri + "&" + param;
					if (i < paramsList.size() - 1) {
						recursivePermutation(paramsList, paramsList.get(i + 1),
								param, queryUris);
					} else if (i == paramsList.size() - 1) {
						queryUris.add(param);
					}
				}
			}
		}
	}

	protected String getQueryString(String name, String value) {
		return name + "=" + value;
	}

	protected void setQueryUrlDoFlag(QueryURL queryURL, int doFlag) {
		queryURL.setDoFlag(doFlag);
	}

	@Override
	public String toPostUrl(String spellUrl) {
		return spellUrl;
	}

	@Override
	public String toSpellUrl(String postUrl) {
		return postUrl;
	}

}
